Objective-C之GCD多线程(一)

前言

什么是GCD?

Grand Central dispatch(GCD)是异步执行任务的技术之一。一般将应用程序中记述的线程管理用的代码在系统级中实现。开发者只需要定义想执行的任务并追加到适当的dispatch Queue中,GCD就能生成必要的线程并计划执行任务。由于线程管理是作为系统的一部分来实现的,因此可以统一管理,也可执行任务,这样就比以前的线程更加有效率。

什么是线程?

  • 线程(thread)是操作系统能够进行运算调度的最小单位。
  • Mac、iPhone的操作系统OS X、iOS根据用户的指示启动应用程序后,首先便将包含在应用程序中的CPU命令列配置到内存中。CPU从应用程序指定的地址开始,一个一个地执行CPU命令列。由于一个CPU一次只能执行一个命令,不能执行某处分开的并列的两个命令,因此通过CPU执行的CPU命令列就是一条路走到黑。这里所说的“1个CPU执行的CPU名列为一条路走到黑”就是“线程”。
  • 现在一个物理的CPU芯片实际上有64(64核)个CPU,即可以拥有多条线程。
  • 多线程编程会产生很多编程技术问题:数据竞争、死锁、内存消耗等问题。
  • 想要更加了解线程、进程等可自行参阅《操作系统原理》这本书。

dispatch Queue

dispatch Queue是执行处理的等待队列。我们可以调用dispatch_async函数等API,在Block语法中记述想执行的处理并追加到dispatch Queue中。dispatch Queue会按照FIFO(先进先出)执行处理。下面的图解释了FIFO

通过dispatch Queue执行处理

在dispatch Queue中存在两种dispatch Queue,一种是等待现在执行中的Serial dispatch Queue,另一种是不等待直接执行的Concurrent dispatch Queue。

dispatch Queue种类 说明
Serial dispatch Queue 等待现在执行中的处理结束后才开始执行
Concurrent dispatch Queue 不等待现在执行中的处理,直接开始执行
Serial dispatch Queue
Concurrent dispatch Queue

因为Serial dispatch Queue是等待执行完成后才开始下一个处理。那么在有多个处理的时候,也是按照先后顺序来的。

int main(int argc, const char * argv[]) {
    // 创建一个SerialQueue
    dispatch_queue_t serialdispatchQueue = dispatch_queue_create("com.larry.gcd.serialTest", DISPATCH_QUEUE_SERIAL);
    // Serial dispatch Queue
    dispatch_async(serialdispatchQueue, ^{printf("1\n");});
    dispatch_async(serialdispatchQueue, ^{printf("2\n");});
    dispatch_async(serialdispatchQueue, ^{printf("3\n");});
    dispatch_async(serialdispatchQueue, ^{printf("4\n");});
    // 防止程序结束
    while (1) {
    }
    return 0;
}

执行结果如下:

Serial Queue执行结果

Concurrent dispatch Queue是不等待执行,那么任何任务添加到Concurrent dispatch Queue后,就会立即执行。但是Concurrent dispatch Queue不是无限制的立即执行当前添加的处理,当前并行执行的处理的数量取决于当前系统的状态。即iOS和OS X基于dispatch Queue中的处理数、CPU数、以及CPU负荷等当前系统状态来决定Concurrent dispatch Queue中并行处理数。

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    // 创建一个Concurrent dispatch Queue
    dispatch_queue_t concurrentdispatchQueue = dispatch_queue_create("com.larry.gcd.serialTest", DISPATCH_QUEUE_CONCURRENT);
    // Serial dispatch Queue
    dispatch_async(concurrentdispatchQueue, ^{NSLog(@"1");});
    dispatch_async(concurrentdispatchQueue, ^{NSLog(@"2");});
    dispatch_async(concurrentdispatchQueue, ^{NSLog(@"3");});
    dispatch_async(concurrentdispatchQueue, ^{NSLog(@"4");});
    dispatch_async(concurrentdispatchQueue, ^{NSLog(@"5");});
    dispatch_async(concurrentdispatchQueue, ^{NSLog(@"6");});
    // 防止程序结束
    while (1) {
    }
    return 0;
}

执行结果:

Concurrent dispatch Queue执行结果

其中执行结果乱序是因为处理添加到线程中的时候,需要等待线程执行完成后,才开始执行。即6个Block添加到6个线程中,6个线程里面都仍然有任务,等任务执行完成后,才开始执行我们添加的处理。

dispatch_queue_create

在Dispatch Queue中,我们使用了dispatch_queue_create来建立一个Queue。
生成Dispatch Queue有两种方法:

  • 通过GCD的API生成dispatch Queue
  • 获取系统标准提供的dispatch Queue
    这里先说明第一种方法,第二种在Main dispatch Queue/Global dispatch Queue中说明。

使用dispatch_queue_create

    // 创建一个Concurrent dispatch Queue
    dispatch_queue_t concurrentdispatchQueue = dispatch_queue_create("com.larry.gcd.serialTest", DISPATCH_QUEUE_CONCURRENT);

    // 创建一个Serial dispatch Queue
    dispatch_queue_t serialdispatchQueue = dispatch_queue_create("com.larry.gcd.serialTest", DISPATCH_QUEUE_SERIAL);

dispatch_queue_create函数有2个参数,第一个参数是dispatch Queue的名称,推荐使用应用程序ID这样的逆序全程域名。该名称在Xcode和Instruments的调试器中作为dispatch Queue的名称表示。第二个参数是指定dispatch Queue的类型,如果填写NULL则默认为DISPATCH_QUEUE_SERIAL。
前文讲到,Serial dispatch Queue同时只能执行一个处理。但是生成多个Serial dispatch Queue时,各个Serial dispatch Queue将并行执行。4个处理追加到4个Serial dispatch Queue中,4个处理将同时执行。

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    // 创建一个SerialQueue
    dispatch_queue_t serialdispatchQueue1 = dispatch_queue_create("com.larry.gcd.serialTest", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialdispatchQueue2 = dispatch_queue_create("com.larry.gcd.serialTest", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialdispatchQueue3 = dispatch_queue_create("com.larry.gcd.serialTest", DISPATCH_QUEUE_SERIAL);
    // Serial dispatch Queue
    dispatch_async(serialdispatchQueue1, ^{NSLog(@"1");});
    dispatch_async(serialdispatchQueue2, ^{NSLog(@"2");});
    dispatch_async(serialdispatchQueue3, ^{NSLog(@"3");});
    // 防止程序结束
    while (1) {
    }
    return 0;
}

执行结果:

多个Serial dispatch Queue同时执行

由执行结果乱序可以知道,多个Serial dispatch Queue是并行执行的!

dispatch_queue_create使用注意

  • 不能无限制的创建Serial Dispatch Queue,会消耗大量的内存,引起大量的上下文切换,大幅度降低系统的响应性能。
  • 在为了避免多个线程对同一个资源进行操作时(数据竞争)使用Serial Dispatch Queue,因为其使用一个线程,数据安全。
  • 当不需要顾忌数据竞争问题时候,推荐使用Concurrent Dispatch Queue。因为不管生成多少,系统会对其进行管理,不用担心Serial Dispatch Queue类似的问题。
  • 最好为每一个Dispatch Queue编写不同的名字,否则你会在调试多线程程序的时候感觉自己仿佛是一个辣鸡。
  • 关于 dispatch_retaindispatch_release 的使用
    • 如果你部署的最低目标低于 iOS 6.0 or Mac OS X 10.8,你应该自己管理GCD对象,使用(dispatch_retain,dispatch_release),ARC并不会去管理它们。
    • 如果你部署的最低目标是 iOS 6.0 or Mac OS X 10.8或者更高,ARC已经能够管理GCD对象了,这时候,GCD对象就如同普通的OC对象一样,不应该使用dispatch_retain或者dispatch_release。

Main Dispatch Queue/Global Dispatch Queue

除了使用dispatch_queue_create,还可以利用系统标准提供的Dispatch Queue。系统提供了2个:

  • Main Dispatch Queue,即主线程中执行的Dispatch Queue,而主线程只有一个,所以Main Dispatch Queue就是Serial Dispatch Queue.
  • Global Dispatch Queue,是所有应用程序都能够使用的Concurrent Dispatch Queue。所以没有必要通过dispatch_queue_create来创建,直接获取Global Dispatch Queue即可。
   /** Main Dispatch Queue的获取 */
   dispatch_queue_t mainDispatchQueue = dispatch_get_main_queue();

   /** Global Dispatch Queue(最高)的获取方法 */
   dispatch_queue_t highGlobalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

   /** Global Dispatch Queue(默认)的获取方法 */
   dispatch_queue_t defaultGlobalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

   /** Global Dispatch Queue(低)的获取方法 */
   dispatch_queue_t losGlobalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);

   /** Global Dispatch Queue(后台,最低)的获取方法 */
   dispatch_queue_t backgroundGlobalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

Global Dispatch Queue

Global Dispatch Queue有4个优先级,分别是高优先级、默认优先级、低优先级和后台优先级。

名称 Dispatch Queue的种类 说明
Main Dispatch Queue Serial Queue 主线程执行
Global Dispatch Queue(High Priority)高优先级 Concurrent Dispatch Queue 执行优先级:最高
Global Dispatch Queue(Default Priority)默认优先级 Concurrent Dispatch Queue 执行优先级:默认
Global Dispatch Queue(Low Priority)低优先级 Concurrent Dispatch Queue 执行优先级:低
Global Dispatch Queue(Background Priority)后台优先级 Concurrent Dispatch Queue 执行优先级:后台(最低)

结语

  • 本文主要讲常用的一些GCD的API,更多内容,请等待下一周更新。
  • 此为《Objective-C 高级编程》的学习笔记。
  • 如有错误,欢迎指正。
  • 如需转载,请注明出处。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,458评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,454评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,171评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,062评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,440评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,661评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,906评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,609评论 0 200
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,379评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,600评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,085评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,409评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,072评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,088评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,860评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,704评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,608评论 2 270

推荐阅读更多精彩内容

  • 3.1 Grand Central Dispatch(GCD)概要 3.1.1 什么是CGD Grand Cent...
    SkyMing一C阅读 1,564评论 0 22
  • 多线程概念 线程线程指的是:1个CPU执行的CPU命令列为一条无分叉路径 多线程这种无分叉路径不止一条,存在多条即...
    我系哆啦阅读 528评论 0 5
  • 1. GCD简介 什么是GCD呢?我们先来看看百度百科的解释简单了解下概念 引自百度百科:Grand Centra...
    千寻_544f阅读 340评论 0 0
  • 估计看完上一篇文章《演绎和归纳》,你该想了,这两个概念我清楚了,但是怎么能用起来,好让自己的思考更深刻呢? 感觉好...
    北北谈教育与学习阅读 304评论 0 0
  • 晴天碧海,微风习习。甲板上,游人如织。舞台上,俄罗斯小伙们的踢踏舞活力无限。蹦蹦哒哒,声声不息。船舱里,斑驳,...
    兰于烟阅读 571评论 0 0